package com.zufangbao.earth.handler.impl;

import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.demo2do.core.entity.Result;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.zufangbao.earth.email.excel.ExcelHandlerSpec;
import com.zufangbao.earth.entity.security.Principal;
import com.zufangbao.earth.handler.PaymentHandler;
import com.zufangbao.sun.entity.Channel;
import com.zufangbao.sun.entity.agency.EscrowContract;
import com.zufangbao.sun.entity.company.corp.App;
import com.zufangbao.sun.entity.company.corp.AppPaymentConfig;
import com.zufangbao.sun.entity.contract.Contract;
import com.zufangbao.sun.entity.factoring.AssetPackage;
import com.zufangbao.sun.entity.factoring.FactoringContract;
import com.zufangbao.sun.entity.finance.BatchPayDetail;
import com.zufangbao.sun.entity.finance.BatchPayRecord;
import com.zufangbao.sun.entity.finance.PayStatus;
import com.zufangbao.sun.entity.finance.PaymentInfo;
import com.zufangbao.sun.entity.finance.PaymentInfoTitles;
import com.zufangbao.sun.entity.finance.ReportDataGenerator;
import com.zufangbao.sun.entity.finance.ReportTitle;
import com.zufangbao.sun.entity.order.Order;
import com.zufangbao.sun.entity.order.OrderStatus;
import com.zufangbao.sun.entity.order.RepaymentType;
import com.zufangbao.sun.entity.order.TransactionRecordTitles;
import com.zufangbao.sun.entity.payment.config.PaymentInstitution;
import com.zufangbao.sun.service.AssetPackageService;
import com.zufangbao.sun.service.BatchPayRecordService;
import com.zufangbao.sun.service.EscrowContractService;

/**
 * 
 * @author zjm
 *
 */
@Component("paymentHandler")
public class PaymentHandlerImpl implements PaymentHandler {

	@Autowired
	private AssetPackageService assetPackageService;
	@Autowired
	private GenericDaoSupport genericDaoSupport;
	@Autowired
	private EscrowContractService escrowContractService;
	@Autowired
	private BatchPayRecordService batchPayRecordService;

	@Override
	public List<PaymentInfo> getPaidOrdersInFactoring() {

		Map<String, Object> parameters = get_query_parameters();
		List<Order> orderList = genericDaoSupport
					.searchForList(
							"FROM Order WHERE contract in(:contracts) AND orderStatus =:orderStatus",
							parameters);
		return change_orders_to_payment_info(orderList);
	}


	
	@Override
	public List<PaymentInfo> getPaidOrdersNotInFactoring() {

		List<Order> orderList = get_paid_orders_not_factoring();
		return change_orders_to_payment_info(orderList);
	}



	private List<Order> get_paid_orders_not_factoring() {
		Map<String, Object> parameters = get_query_parameters();

		List<Order> orderList = genericDaoSupport
				.searchForList(
						"FROM Order WHERE contract not in(:contracts) AND orderStatus =:orderStatus",
						parameters);
		return orderList;
	}
	
	

	private Map<String, Object> get_query_parameters() {
		List<AssetPackage> assetPackages = genericDaoSupport
				.loadAll(AssetPackage.class);

		return create_paid_orders_contracts_params(assetPackages);
	}





	@Override
	public List<PaymentInfo> getPaidOrdersByApp(Long appId) {
		
		List<Order> orderList = new ArrayList<Order>();
		List<Order> not_factoring_orders = get_paid_orders_not_factoring();
		for(Order order : not_factoring_orders){
			if(appId.equals(order.getContract().getApp().getId())){
				orderList.add(order);
			}
		}
		
		return change_orders_to_payment_info(orderList);
	}
	
	
	@Override
	public List<PaymentInfo> getOrdersByFactoringContract(Long factoringContractId,
			RepaymentType repaymentType) {

		List<AssetPackage> assertPackages = assetPackageService
				.getAssetPackageByFactoringContract(factoringContractId);

		Map<String, Object> parameters = create_paid_orders_contracts_params(assertPackages);
		parameters.put("repaymentType", repaymentType);

		List<Order> orderList = genericDaoSupport
				.searchForList(
						"FROM Order WHERE contract in(:contracts) AND repaymentType =:repaymentType AND orderStatus =:orderStatus",
						parameters);
		return change_orders_to_payment_info(orderList);
	}



	private Map<String, Object> create_paid_orders_contracts_params(List<AssetPackage> assertPackages) {
		Contract[] contracts = get_contracts_by_assertPackages(assertPackages);
		Map<String, Object> parameters = new HashMap<String, Object>();
		parameters.put("contracts", contracts);
		parameters.put("orderStatus", OrderStatus.PAID);
		return parameters;
	}

	private Contract[] get_contracts_by_assertPackages(
			List<AssetPackage> assertPackages) {

		Contract[] contracts = new Contract[assertPackages.size()];

		for (int i = 0; i < assertPackages.size(); i++) {
			contracts[i] = assertPackages.get(i).getContract();
		}
		return contracts;
	}

	@Override
	public Result applyBatchPayForFactoring(String orderIds, String remark,
			Principal principal) {
		Result result = new Result();
		try {
			List<Order> orders_checked = get_orders_by_ids(orderIds);

			if (!can_be_merged_of_factoring(orders_checked)) {
				return get_error_result(result);
			}

			create_batch_pay_record_and_details(remark, principal, result,
					orders_checked, true);

		} catch (Exception e) {
			e.printStackTrace();
		}
		result.success();
		return result;
	}



	private void create_batch_pay_record_and_details(String remark,
			Principal principal, Result result, List<Order> orders_checked, boolean is_factoring) {
		AppPaymentConfig appPaymentConfig = get_payment_config_by_app(orders_checked
				.get(0));

		BatchPayRecord batchPayRecord = create_batch_payment_record(remark,
				principal, orders_checked, appPaymentConfig);

		update_order_and_create_pay_detail(principal, orders_checked,
				batchPayRecord);

		if(is_factoring){
			judge_directbank_and_set_parameters(result, appPaymentConfig,
					batchPayRecord);
		}
		
	}
	
	
	@Override
	public Result applyBatchPayForAgency(String orderIds, String remark,
			Principal principal) {
		Result result = new Result();
		try {
			List<Order> orders_checked = get_orders_by_ids(orderIds);

			if (!can_be_merged_of_agency(orders_checked)) {
				return get_error_result(result);
			}

			if(!exist_agency_contract(orders_checked.get(0))){
				result.fail();
				result.setMessage("该商户尚未签订代收付协议！");
				return result;
			}
			
			create_batch_pay_record_and_details(remark, principal, result,
					orders_checked, false);

		} catch (Exception e) {
			e.printStackTrace();
		}
		result.success();
		return result;
	}
	
	private boolean exist_agency_contract(Order order){
		EscrowContract escrowContract = escrowContractService.getEscrowContractByApp(order.getContract().getApp());
		return escrowContract != null;
	}
	

	private AppPaymentConfig get_payment_config_by_app(Order order) {
		return (AppPaymentConfig) genericDaoSupport.searchForList(
				"FROM AppPaymentConfig WHERE app =:app", "app",
				order.getContract().getApp()).get(0);
	}

	private void judge_directbank_and_set_parameters(Result result,
			AppPaymentConfig appPaymentConfig, BatchPayRecord batchPayRecord) {
		if (!Channel.DIRECTBANK.equals(appPaymentConfig.getChannel())) {
			return;
		}
		Map<String, Object> parameters = new HashMap<String, Object>();
		parameters.put("amount", batchPayRecord.getAmount());
		parameters.put("receiveAccountName", batchPayRecord.getReceiveAccount()
				.getAccountName());
		parameters.put("receiveAccountNo", batchPayRecord.getReceiveAccount()
				.getAccountNo());
		parameters.put("remark", batchPayRecord.getRemark());
		parameters.put("batchPayRecordId", batchPayRecord.getId());
		result.setData(parameters);

	}

	private void update_order_and_create_pay_detail(Principal principal,
			List<Order> orders_checked, BatchPayRecord batchPayRecord) {
		for (Order order : orders_checked) {
			BatchPayDetail batchPayDetail = new BatchPayDetail(order,
					batchPayRecord, order.getTotalRent(), new Date(),
					principal.getId());

			genericDaoSupport.update(order.toBeApplied());
			genericDaoSupport.save(batchPayDetail);
		}
	}

	private BatchPayRecord create_batch_payment_record(String remark,
			Principal principal, List<Order> orders_checked,
			AppPaymentConfig appPaymentConfig) {
		BigDecimal batch_payment_amount = BigDecimal.ZERO;
		for (Order order : orders_checked) {
			batch_payment_amount = batch_payment_amount.add(order
					.getTotalRent());
		}

		PaymentInstitution paymentInstitution = appPaymentConfig
				.getPaymentInstitution();

		BatchPayRecord batchPayRecord = create_batch_record(remark,
				principal, orders_checked.get(0), batch_payment_amount,
				paymentInstitution);
		genericDaoSupport.save(batchPayRecord);
		return batchPayRecord;
	}



	private BatchPayRecord create_batch_record(String remark,
			Principal principal, Order order,
			BigDecimal batch_payment_amount,
			PaymentInstitution paymentInstitution) {

		PaymentInfo paymentInfo =  change_order_to_paymentInfo(order);
		
		BatchPayRecord batchPayRecord = new BatchPayRecord(
				batch_payment_amount, paymentInstitution, PayStatus.NotPayed,
				remark, principal.getId(), new Date(), new Date(),
				paymentInfo.getPayerAccount(), paymentInfo.getReceiveAccount());
		return batchPayRecord;
	}

	private List<Order> get_orders_by_ids(String orderIds) {
		Long[] orders = trans_orderIds_to_long(orderIds);

		List<Order> orders_checked = genericDaoSupport.searchForList(
				"FROM Order WHERE id in(:orders)", "orders", orders);
		return orders_checked;
	}

	private boolean is_same_factoring_contract(List<Order> orders_checked) {
		if (orders_checked.isEmpty()) {
			return true;
		}
		FactoringContract factoringContract = getFactoringContract(orders_checked
				.get(0));

		for (Order order : orders_checked) {
			if (!factoringContract.equals(getFactoringContract(order))) {
				return false;
			}
		}
		return true;
	}

	private FactoringContract getFactoringContract(Order order) {
		AssetPackage assetPackage = assetPackageService.getAssetPackagesByContract(order.getContract());
		
		if(null == assetPackage){
			return null;
		}
		
		return assetPackage.getFactoringContract();
	}

	private Result get_error_result(Result result) {
		result.fail();
		result.setMessage("您勾选的订单无法进行合并打款！");
		return result;
	}

	private boolean is_same_repayment_type(List<Order> orders_checked) {
		if (orders_checked.isEmpty()) {
			return true;
		}
		RepaymentType repaymentType = orders_checked.get(0).getRepaymentType();
		for (Order order : orders_checked) {
			if (!repaymentType.equals(order.getRepaymentType())) {
				return false;
			}
		}
		return true;
	}

	private boolean can_be_merged_of_factoring(List<Order> orders_checked) {
		if (!is_same_repayment_type(orders_checked)) {
			return false;
		}

		if (!is_same_factoring_contract(orders_checked)) {
			return false;
		}
		return true;
	}
	
	private boolean can_be_merged_of_agency(List<Order> orders_checked) {
		if (orders_checked.isEmpty()) {
			return true;
		}
		App app = orders_checked.get(0).getContract().getApp();
		for (Order order : orders_checked) {
			if (!app.equals(order.getContract().getApp())) {
				return false;
			}
		}
		return true;
	}

	private Long[] trans_orderIds_to_long(String orderIds) {
		String[] arr_orderId = orderIds.split(",");
		Long[] orders = new Long[arr_orderId.length];
		for (int i = 0; i < arr_orderId.length; i++) {
			orders[i] = Long.parseLong(arr_orderId[i]);
		}
		return orders;
	}


	private List<PaymentInfo> change_orders_to_payment_info(List<Order> orderList){
		List<PaymentInfo> paymentInfoList = new ArrayList<PaymentInfo>();
		for(Order order : orderList){
			
			PaymentInfo paymentInfo = change_order_to_paymentInfo(order);
			
			paymentInfoList.add(paymentInfo);
		}
		
		return paymentInfoList;
	}



	private PaymentInfo change_order_to_paymentInfo(Order order) {
		PaymentInfo paymentInfo = new PaymentInfo();
		paymentInfo.setOrder(order);
		FactoringContract factoringContract = getFactoringContract(order);
		if(factoringContract == null){
			EscrowContract escrowContract = escrowContractService.getEscrowContractByApp(order.getContract().getApp());
			if(escrowContract == null){
				return paymentInfo;
			}	
			paymentInfo.setReceiveAccount(escrowContract.getEscrowAgreement().getRepaymentAccount());
			paymentInfo.setPayerAccount(escrowContract.getEscrowAgreement().getEscrowAccount());
			return paymentInfo;
		}
		
			if(order.isRiskReserve()){
				paymentInfo.setReceiveAccount(factoringContract.getPaymentAgreement().getRiskReserveAccount());
			}else{
				paymentInfo.setReceiveAccount(factoringContract.getPaymentAgreement().getRepaymentAccount());
			}
			paymentInfo.setPayerAccount(factoringContract.getPaymentAgreement().getZfbAccount());
		
		return paymentInfo;
	}



	@Override
	public List<PaymentInfo> changeBatchPayDetailsToPaymentInfo(
			List<BatchPayDetail> batchPayDetailList) {
		
		List<Order> orderList = new ArrayList<Order>();
		for(BatchPayDetail batchPayDetail : batchPayDetailList){
			orderList.add(batchPayDetail.getOrder());
		}
		
		return change_orders_to_payment_info(orderList);
	}



	@Override
	public void createDetailExcel(Long batchPayRecordId, OutputStream output) throws IOException {
		
		List<ReportDataGenerator> reportDataGenerator = new ArrayList<ReportDataGenerator>();
		
		BatchPayRecord batchPayRecord = batchPayRecordService.load(BatchPayRecord.class, batchPayRecordId);
		List<BatchPayDetail> batchPayDetails = batchPayRecordService.getDetailByBatchPay(batchPayRecordId);
		List<PaymentInfo> paymentInfoList = this.changeBatchPayDetailsToPaymentInfo(batchPayDetails);
		
		for(PaymentInfo paymentInfo : paymentInfoList){
			reportDataGenerator.add(paymentInfo);
		}
		
		Workbook excelWorkBook = new HSSFWorkbook();
		Sheet sheet = excelWorkBook.createSheet(ExcelHandlerSpec.PAYMENT_DETAIL);
		
		
		create_table_title_row(PaymentInfoTitles.values(), sheet, excelWorkBook);
		int last_row_index = create_table_data_rows(reportDataGenerator, PaymentInfoTitles.values(),
				excelWorkBook, sheet);
		
		create_table_summary_row(batchPayRecord.getAmount(), sheet, last_row_index);
		
		output.flush(); 
		excelWorkBook.write(output);
		output.close();
		
	}
	
	
	public void create_table_title_row(PaymentInfoTitles[] titles,
			Sheet sheet, Workbook excelWorkBook) {
		HSSFCellStyle titleStyle = create_title_style(excelWorkBook);
		Row titleRow = sheet.createRow((short) 0);
		for(ReportTitle title_key: titles){
			Cell cell = titleRow.createCell(title_key.ordinal());
			cell.setCellStyle(titleStyle);
			cell.setCellValue(title_key.getKey());
			sheet.autoSizeColumn(title_key.ordinal());
		}
	}
	
	public HSSFCellStyle create_title_style(Workbook excelWorkBook) {
		HSSFFont fontForReportTitle = (HSSFFont) excelWorkBook.createFont();
		fontForReportTitle.setFontName(ExcelHandlerSpec.FONT_SETTING_FOR_REPORT_TITLE);
		fontForReportTitle.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
		fontForReportTitle.setFontHeightInPoints(ExcelHandlerSpec.FONT_SIZE);
		HSSFCellStyle titleStyle = (HSSFCellStyle) excelWorkBook.createCellStyle();
		titleStyle.setFont(fontForReportTitle);
		return titleStyle;
	}
	
	
	public int create_table_data_rows(
			List<ReportDataGenerator> reportDataGenerator,
			PaymentInfoTitles[] titles, Workbook excelWorkBook,
			Sheet sheet) {
		HSSFCellStyle contentStyle = create_excel_content_style(excelWorkBook);
		int data_index = 0;
		for(;data_index<reportDataGenerator.size();data_index++){
			create_table_data_rows(titles, sheet, contentStyle,
					data_index, reportDataGenerator.get(data_index));
		}
		return data_index+ ExcelHandlerSpec.TABLE_DATA_ROW_BASE_INDEX ;
	}
	
	public HSSFCellStyle create_excel_content_style(Workbook excelWorkBook) {
		HSSFFont fontForReportContent = (HSSFFont) excelWorkBook.createFont();
		fontForReportContent.setFontName(ExcelHandlerSpec.FONT_SETTING_FOR_REPORT_CONTENT);
		fontForReportContent.setFontHeightInPoints(ExcelHandlerSpec.FONT_SIZE);
		HSSFCellStyle contentStyle = (HSSFCellStyle) excelWorkBook.createCellStyle();
		contentStyle.setFont(fontForReportContent);
		return contentStyle;
	}
	
	public void create_table_data_rows(PaymentInfoTitles[] titles,
			Sheet sheet, HSSFCellStyle contentStyle, int data_index,
			ReportDataGenerator paymentInfo) {
		
		Row contentRow = sheet.createRow((short) data_index + ExcelHandlerSpec.TABLE_DATA_ROW_BASE_INDEX);
		Map<String,String> reportData=paymentInfo.toReportData();
		for(PaymentInfoTitles title_key: titles){
			Cell cell = contentRow.createCell(title_key.ordinal());
			cell.setCellStyle(contentStyle);
			cell.setCellValue(reportData.get(title_key.getKey()));
			
			if(title_key.getKey().equals(TransactionRecordTitles.PAYMENT_MONEY.getKey())){
				cell.setCellValue(Double.parseDouble(reportData.get(title_key.getKey())));
			}
			
			
			sheet.autoSizeColumn(title_key.ordinal());
		}
	}
	
	
	public void create_table_summary_row(BigDecimal totalAmount,
			Sheet sheet, int last_row_index) {
		Row endRow = sheet.createRow((short) last_row_index);
		Cell totalCell = endRow.createCell(4);
		totalCell.setCellValue("合计");
		Cell totalcount = endRow.createCell(5);
		totalcount.setCellValue(Double.valueOf(totalAmount.toString()));
	}
	
}
